# -*- coding: utf-8 -*-

from hlm.node import *

from ip.fabrics import ChannelLoadReqFabric, ChannelLoadFabric, ChannelStoreFabric


class NodeMainMemory(Node):
    def initialize(self, params: ObjectDict) -> None:
        params.cas: int
        params.multiplexer: int
        params.size: int
        # Save the latency.
        self.const.cas = params.cas
        self.const.multiplexer = params.multiplexer
        # Initialized the ports.
        self.port.request_receiver = self.port_channel_receiver(ChannelLoadReqFabric)
        self.port.load_sender = self.port_channel_sender(ChannelLoadFabric)
        self.port.store_receiver = self.port_channel_receiver(ChannelStoreFabric)

        # Prepare the memory space.
        self.reg.data = [0j] * params.size
        # Create dummy queues for simulate the CAS.
        self.reg.queue_write = []
        self.reg.queue_read = []

    def preload(self, aops: list):
        # Loop and set the data.
        for aop in aops:
            self.reg.data[aop.addr] = aop.data

    def flush(self):
        # Loop for all the write state.
        for queue_request in self.reg.queue_write:
            self.reg.data[queue_request.addr] = queue_request.data

    tasks = ['task_check_read',
             'task_check_write']

    def task_check_read(self):
        # Check the read queue.
        if self.reg.queue_read and not self.port.load_sender.Bus.is_any_full():
            # Update queue information.
            for ii in range(len(self.reg.queue_read) - 1, -1, -1):
                # Check the cas count.
                if self.reg.queue_read[ii].cas_count <= 0:
                    self.port.load_sender.Bus.put(
                        pid=self.reg.queue_read[ii].pid,
                        addr=self.reg.queue_read[ii].addr,
                        data=self.reg.queue_read[ii].data,
                        sb_addr=self.reg.queue_read[ii].sb_addr,
                        ain_addr=self.reg.queue_read[ii].ain_addr,
                        clr=self.reg.queue_read[ii].clr
                    )
                    # Remove the data.
                    self.reg.queue_read.pop(ii)
                else:
                    self.reg.queue_read[ii].cas_count -= 1
        # Loop for multiplex times for simulation.
        for _ in range(self.const.multiplexer):
            if not self.port.request_receiver.Bus.is_any_empty():
                mem_addr, pid, sm_addr, sb_addr, ain_addr, clr = \
                    self.port.request_receiver.Bus.get()
                # Append the new data.
                request = ObjectDict()
                request.data = self.reg.data[mem_addr]
                request.addr = sm_addr
                request.pid = pid
                request.sb_addr = sb_addr
                request.ain_addr = ain_addr
                request.clr = clr
                request.cas_count = self.const.cas
                self.reg.queue_read.append(request)

    def task_check_write(self):
        # Check the write request queue.
        if self.reg.queue_write:
            for ii in range(len(self.reg.queue_write) - 1, -1, -1):
                # Check the cas count.
                if self.reg.queue_write[ii].cas_count <= 0:
                    # Update the data.
                    self.reg.data[self.reg.queue_write[ii].addr] = self.reg.queue_write[ii].data
                    self.reg.queue_write.pop(ii)
                else:
                    self.reg.queue_write[ii].cas_count -= 1

        for _ in range(self.const.multiplexer):
            if not self.port.store_receiver.Bus.is_any_empty():
                addr, data = self.port.store_receiver.Bus.get()
                # Append the new data.
                request = ObjectDict()
                request.addr = addr
                request.data = data
                request.cas_count = self.const.cas
                self.reg.queue_write.append(request)
